package com.rd.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rd.common.vo.AdminStatusVO;
import com.rd.common.vo.UpdateAvatarFormData;
import com.rd.common.vo.UpdatePwdFormData;
import com.rd.config.security.JwtTokenUtil;
import com.rd.mapper.AdminMapper;
import com.rd.mapper.AdminRoleMapper;
import com.rd.mapper.MenuMapper;
import com.rd.mapper.RoleMapper;
import com.rd.pojo.Admin;
import com.rd.pojo.AdminRole;
import com.rd.pojo.Menu;
import com.rd.pojo.Role;
import com.rd.service.IAdminService;
import com.rd.service.IMenuService;
import com.rd.util.ResultModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author qiufenfen
 * @since 2023-10-26
 */
@Service
@Slf4j
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {

    /**UserDetailsService是Spring Security加载用户数据的核心接口，
     * 提供了根据用户名查找用户的方法。
     在Spring Security中，UserDetails表示用户的核心信息，
     如权限集、密码、用户名、账户是否过期、账户是否锁定、凭证是否过期、用户是否可用等。
     在实际开发中，需要扩展UserDetails来自定义存储更多的用户信息*/
    /**ctrl+alt+shift+n*/
    @Resource
    private UserDetailsService userDetailsService;

    /**Spring Security提供判断密码接口*/
    @Resource
    private PasswordEncoder passwordEncoder;

    /**生成和验证jwt工具*/
    @Resource
    private JwtTokenUtil jwtTokenUtil;

    /**从配置文件中获取值*/
    @Value("${jwt.tokenHead}")
    private String tokenHead;


    @Resource
    private RoleMapper roleMapper;

    @Resource
    private IMenuService menuService;

    /**用户登录*/
    @Override
    public ResultModel login(String username, String password, String code, HttpServletRequest request) {
        //从session中获取验证码
        String captcha = (String) request.getSession().getAttribute("captcha");
        System.out.println("从session中获取的验证码:" +captcha);
        if(code==null || !captcha.equalsIgnoreCase(code)){
            return ResultModel.createByErrorCodeMessage("验证码错误！");
        }

        //1.调用loadUserByUsername
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if(userDetails==null  || !passwordEncoder.matches(password,userDetails.getPassword()) ){
            return ResultModel.createByErrorCodeMessage("用户名或者密码错误!");
        }

        if(!userDetails.isEnabled()){
            return ResultModel.createByErrorCodeMessage("账户禁用,请联系管理员!");
        }

        //2.更新security中的用户对象
        //处理基于表单或HTTP基本认证的登录请求，将用户提交的用户名和密码封装，
        // 并创建一个包含认证信息的UsernamePasswordAuthenticationToken对象
        UsernamePasswordAuthenticationToken  authenticationToken=new
                UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());

        //把认证后的authenticationToken存储在上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        //3.生成token值
        String token = jwtTokenUtil.generateToken(userDetails);

        //4.获取当前登录的用户信息
        Admin admin = getOne(new QueryWrapper<Admin>().eq("username", username));

        //5.获取用户的权限菜单
        List<Menu> menuList = menuService.getMenusByAdminId(admin.getId());

        //4.将token值传递给前端
        return ResultModel.createBySuccess("登录成功!").put("token",token).put("tokenHead",tokenHead)
                .put("adminInfo",admin).put("menuList",menuList);
    }

    /**根据用户名获取用户详细信息*/
    @Override
    public Admin getAdminInfo(String username) {
        return getOne(new QueryWrapper<Admin>().eq("username",username));
    }

    /**根据用户id获取用户角色*/
    @Override
    public List<Role> getRoles(Integer adminId) {
        return roleMapper.getRoles(adminId);
    }


    @Resource
    AdminMapper adminMapper;

    @Resource
    AdminRoleMapper adminRoleMapper;
    /**
     * @return
     * @description: 获取所有操作员
     * @param: keywords
     */
    @Override
    public ResultModel getAllAdmins(String keywords) {
        //获取当前登录的用户,需要排除当前登录的用户
        Admin admin = (Admin) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        List<Admin> adminList = adminMapper.getAllAdmins(admin.getId(), keywords);
        return ResultModel.createBySuccess().put("adminList",adminList);
    }


    @Resource
    RedisTemplate redisTemplate;

    @Resource
    MenuMapper menuMapper;
    /**
     * @return
     * @description: 跟新操作员角色
     * @param: adminId
     * @param: rids
     */
    @Override
    /**声明式，事务管理*/
    @Transactional
    public ResultModel updateAdminRole(Integer adminId, Integer[] rids) {
        //先删除这个用户所拥有所有角色
        // delete from t_admin_role where adminId=?
        adminRoleMapper.delete(new QueryWrapper<AdminRole>().eq("adminId", adminId));
        // 再添加，批量添加
        Integer result = adminRoleMapper.addAdminRole(adminId, rids);
        if (rids.length == result) {
            //定义一个方法，这个方法更新这个用户在redis存储的权限值
            updateRedisMenu(adminId);
            return ResultModel.createBySuccess("更新成功！");
        }
        return ResultModel.createByErrorCodeMessage("更新失败！");
    }

    @Override
    public ResultModel updateStatusById(AdminStatusVO adminStatusVO) {
        Integer num = adminMapper.updateStatusById(adminStatusVO.getId(), adminStatusVO.getEnabled());
        if (num>0) {
            return ResultModel.createBySuccess("更新成功！");
        }
        return ResultModel.createByErrorCodeMessage("更新失败！");
    }

    public void updateRedisMenu(Integer adminId){
        //从redis获取这个用户的权限信息
        Map<String,String> mapFromRedis =redisTemplate.opsForHash().entries("menu_" + adminId);
        if(mapFromRedis!=null && mapFromRedis.size()>0) {
            //说明这个用户登录之前在redis中存储自己的权限缓存
             //先删除
            Set<String> set = mapFromRedis.keySet();
            for (String s : set) {
                redisTemplate.opsForHash().delete("menu_"+adminId,s);
            }
            //再从数据库中查询
            List<Menu>   list = menuMapper.getMenusByAdminId(adminId);
            //操作Hash值
            Map<String,String> temp =new HashMap<>();
            //遍历list集合,将数据存入到temp集合中
            list.forEach(user->{temp.put(String.valueOf(user.getId()),JSONUtil.toJsonStr(user));});
            redisTemplate.opsForHash().putAll("menu_" + adminId,temp);
        }
    }


    /*个人中心*/
    @Override
    public ResultModel updateAdminInfo(Admin admin) {
        Integer num = adminMapper.updateAdminInfo(admin);
        if (num>0) {
            return ResultModel.createBySuccess("更新成功！");
        }
        return ResultModel.createByErrorCodeMessage("更新失败！");
    }


    @Override
    public ResultModel checkOldPwd(Integer userId, String oldPassword) {
        String adminPwd=adminMapper.checkOldPwd(userId);
        log.info(adminPwd);
        BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
        boolean flag = bCryptPasswordEncoder.matches(oldPassword,adminPwd);
        log.info(String.valueOf(flag));
        if (flag){
            return ResultModel.createBySuccess("旧密码输入正确");
        }else {
            return ResultModel.createByErrorCodeMessage("旧密码错误,请重新输入");
        }
    }

    @Override
    public int updateAvatar(UpdateAvatarFormData imgForm) {
        Integer num = adminMapper.updateAvatar(imgForm);
        return num;
    }

    @Override
    public int updatePwd(UpdatePwdFormData pwdForm) {
        Admin admin = new Admin();
        admin.setId(Math.toIntExact(pwdForm.getId()));
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        admin.setPassword(bCryptPasswordEncoder.encode(pwdForm.getNewPassword()));
        return adminMapper.updatePwd(admin);
    }
}
